package cn.lycode.system.service.impl;

import cn.lycode.common.constant.Constants;
import cn.lycode.common.core.domain.entity.SysMenu;
import cn.lycode.common.core.domain.model.ConditionDTO;
import cn.lycode.common.core.domain.model.SelectIdsDTO;
import cn.lycode.common.core.service.Treeable;
import cn.lycode.common.enums.TrueFalseEnum;
import cn.lycode.common.event.EventPublisher;
import cn.lycode.common.event.SessionChangeMeta;
import cn.lycode.common.event.SessionChangeEvent;
import cn.lycode.common.utils.StringUtils;
import cn.lycode.system.domain.dto.sysmenu.MenuPermissionDTO;
import cn.lycode.system.domain.vo.MenuTreeVO;
import cn.lycode.system.domain.vo.SysMenuVO;
import cn.lycode.system.mapper.SysMenuMapper;
import cn.lycode.system.mapper.SysRoleMenuMapper;
import cn.lycode.system.service.SysMenuService;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Title: SysMenuServiceImpl
 * @Author lyg
 * @Date 2024/6/28 18:29
 * @description: 菜单服务实现类
 */
@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl implements SysMenuService {
    private final SysMenuMapper menuMapper;
    private final SysRoleMenuMapper roleMenuMapper;
    private final EventPublisher eventPublisher;

    @Override
    public List<SysMenuVO> selectFrontMenuAll() {
        return buildTreeList(menuMapper.selectFrontMenuAll());
    }

    @Override
    public List<SysMenuVO> selectFrontMenusByRoleName(String roleName) {
        List<SysMenu> sysMenus = menuMapper.selectFrontMenusByRoleName(roleName);
        return buildTreeList(sysMenus);
    }

    @Override
    public Set<String> selectMenuPermsByRoleFlag(String roleName) {
        List<String> perms = menuMapper.selectMenuPermsByRoleName(roleName);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            if (StringUtils.isNotEmpty(perm)) {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }
        return permsSet;
    }

    /**
     * 获取简单菜单树
     */
    @Override
    public List<MenuTreeVO> getSimpleMenuTree() {
        // 创建根目录节点并将所有数据包裹在其中
        MenuTreeVO root = new MenuTreeVO();
        root.setId(0L); // 根目录ID通常为0
        root.setPid(-1L); // 设置一个无效的值作为根目录的PID
        root.setTitle("根目录"); // 根目录的标题

        //不拿按钮，不拿删除的，其他都要（隐藏的、禁用的等）
        List<SysMenu> sysMenus = menuMapper.selectAllMenuExBtn();
        List<MenuTreeVO> menuTreeVOList = Treeable.buildTreeVO(copyMenuToTreeVO(sysMenus));
        root.setChildren(menuTreeVOList);
        return Collections.singletonList(root);
    }

    @Override
    public List<MenuTreeVO> queryRoleMenuTree() {
        //需要拿按钮、隐藏的、禁用的 不拿删除的
        List<SysMenu> sysMenus = menuMapper.selectAllMenu();
        return Treeable.buildTreeVO(copyMenuToTreeVO(sysMenus));
    }

    @NotNull
    private static List<MenuTreeVO> copyMenuToTreeVO(List<SysMenu> sysMenus) {
        List<MenuTreeVO> temp = new ArrayList<>();
        for (SysMenu sysMenu : sysMenus) {
            MenuTreeVO menuTreeVO = new MenuTreeVO();
            menuTreeVO.setId(sysMenu.getId());
            menuTreeVO.setPid(sysMenu.getPid());
            menuTreeVO.setDeep(Long.valueOf(sysMenu.getDeep()));
            menuTreeVO.setSort(Long.valueOf(sysMenu.getMenuSort()));
            menuTreeVO.setTitle(sysMenu.getTitle());
            temp.add(menuTreeVO);
        }
        return temp;
    }

    @Override
    public boolean hasExistsPerms(MenuPermissionDTO dto) {
        int count = menuMapper.countPerms(dto.getPerms(), dto.getId());
        return count > 0;
    }


    /**
     * *******************************************辅助私有方法*********************************************
     */

    /**
     * 是否是根节点
     *
     * @param pid 父级Id
     * @return true:是根节点
     */
    private boolean isRoot(Long pid) {
        return pid == null || pid == 0;
    }
    /**
     * 获取父级跟节点
     *
     * @param list
     * @return
     */
    private List<SysMenuVO> getRootNodes(List<SysMenu> list) {
        List<SysMenuVO> rootList = new ArrayList<>();
        for (SysMenu sysMenu : list) {
            // 找到所有父级节点
            if (isRoot(sysMenu.getPid())) {
                SysMenuVO sysMenuTreeVO = new SysMenuVO();
                BeanUtils.copyProperties(sysMenu, sysMenuTreeVO);
                rootList.add(sysMenuTreeVO);
            }
        }
        return rootList;
    }
    private List<SysMenuVO> buildTreeList(List<SysMenu> sysMenulist) {
        List<SysMenuVO> treeList = new ArrayList<>();
        // 构建树形
        for (SysMenuVO rootNode : getRootNodes(sysMenulist)) {
            SysMenuVO menuVO = new SysMenuVO();
            BeanUtils.copyProperties(rootNode, menuVO);
            SysMenuVO.Meta meta = new SysMenuVO.Meta();
            BeanUtils.copyProperties(rootNode, meta);
            meta.setIsLink(rootNode.getIsLink().equals(TrueFalseEnum.T) ? rootNode.getRedirect() : "");
            menuVO.setMeta(meta);
            SysMenuVO childrenNode = getChildrenNode(menuVO, sysMenulist);
            treeList.add(childrenNode);
        }
        return treeList;
    }
    private SysMenuVO getChildrenNode(SysMenuVO sysMenu, List<SysMenu> menuList) {
        List<SysMenuVO> childrenList = new ArrayList<>();
        for (SysMenu menu : menuList) {
            if (menu.getPid().equals(sysMenu.getId())) {
                SysMenuVO childrenNode = new SysMenuVO();
                BeanUtils.copyProperties(menu, childrenNode);
                SysMenuVO.Meta meta = new SysMenuVO.Meta();
                BeanUtils.copyProperties(menu, meta);
                meta.setIsLink(menu.getIsLink().equals(TrueFalseEnum.T) ? childrenNode.getRedirect() : "");
                childrenNode.setMeta(meta);
                childrenList.add(getChildrenNode(childrenNode, menuList));
            }
        }
        sysMenu.setChildren(childrenList);
        return sysMenu;
    }

    /**
     * *******************************************增删改查*********************************************
     */
    @Override
    public List<SysMenuVO> selectMenuList(ConditionDTO conditionDTO) {
        List<SysMenu> sysMenus = menuMapper.selectMenuList(conditionDTO);
        return buildTreeList(sysMenus);
    }

    @Override
    public SysMenu selectMenuById(Long id) {
        return menuMapper.selectMenuById(id);
    }

    @Override
    public int addMenu(SysMenu menu) {
        int ret = menuMapper.insertMenu(menu);
        clearRoleMenuCache(Collections.singletonList(menu.getId()));
        return ret;
    }

    /**
     * 删除
     *
     * @param dto dto
     */
    @Transactional
    @Override
    public void remove(SelectIdsDTO dto) {
        if (StringUtils.isNotNull(dto.getIds())) {
            // 递归查询下边的子节点id
            Set<Long> allIds = new HashSet<>(dto.getIds());
            findChildrenIds(dto.getIds(), allIds);
            menuMapper.updateMenuAndChildrenIsDelete(new ArrayList<>(allIds));
            //更新层级
            menuMapper.syncTreeDeep();
            //更新是否存在子菜单
            menuMapper.syncTreeHasChildren();
            //同时删除角色菜单关联内容  TODO-final 有关联的角色的菜单最好禁止删除
            roleMenuMapper.deleteRoleMenuByMenuIds(new ArrayList<>(allIds));
            clearRoleMenuCache(dto.getIds());
        }
    }

    private void findChildrenIds(List<Long> parentIds, Set<Long> allIds) {
        if (parentIds.isEmpty()) {
            return;
        }
        List<SysMenu> children = menuMapper.selectMenuByPidList(parentIds);
        List<Long> childrenIds = children.stream()
                .map(SysMenu::getId)
                .collect(Collectors.toList());
        if (!childrenIds.isEmpty()) {
            allIds.addAll(childrenIds);
            findChildrenIds(childrenIds, allIds);
        }
    }

    @Override
    public int updateMenu(SysMenu menu) {
        int ret = menuMapper.updateMenu(menu);
        clearRoleMenuCache(Collections.singletonList(menu.getId()));
        return ret;
    }


    /**
     * 查找菜单相关的所有角色，发布角色权限变更
     * @param menuIds 菜单ids
     */
    private void clearRoleMenuCache(List<Long> menuIds){
        List<String> roleNameList = menuMapper.selectRoleNameListByMenuId(menuIds);
        roleNameList.add(Constants.SUPER_ADMIN);
        eventPublisher.publish(new SessionChangeEvent(this, SessionChangeMeta.roleList(roleNameList)));
    }

}
